package cn.zhaopin.starter.security.handler;

import cn.zhaopin.starter.security.basic.CodeException;
import cn.zhaopin.starter.security.cache.SecurityCacheUtil;
import cn.zhaopin.starter.security.common.JWTUtils;
import cn.zhaopin.starter.security.common.SecurityConstant;
import cn.zhaopin.starter.security.properties.JwtProperties;
import io.jsonwebtoken.Claims;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.core.Authentication;
import org.springframework.security.web.server.WebFilterExchange;
import org.springframework.security.web.server.authentication.logout.ServerLogoutHandler;
import org.springframework.util.StringUtils;
import reactor.core.publisher.Mono;

import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Objects;

/**
 * description:自定义登出
 *
 * @author: zuomin (myleszelic@outlook.com)
 * @date 2021/10/18:21:13
 */
public class BasicServerLogoutHandler implements ServerLogoutHandler {

    private SecurityCacheUtil securityCacheUtil;

    private final JwtProperties jwtProperties;

    public BasicServerLogoutHandler(SecurityCacheUtil securityCacheUtil, JwtProperties jwtProperties) {
        this.securityCacheUtil = securityCacheUtil;
        this.jwtProperties = jwtProperties;
    }

    @Override
    public Mono<Void> logout(WebFilterExchange exchange, Authentication authentication) {
        ServerHttpRequest request = exchange.getExchange().getRequest();

        String token;

        String headerToken = request.getHeaders().getFirst(SecurityConstant.TOKEN_HEADER);
        String paramToken = request.getQueryParams().getFirst(SecurityConstant.TOKEN_PARAM);

        if (StringUtils.hasLength(headerToken)) {
            token = headerToken;
            try {
                token = URLDecoder.decode(token, StandardCharsets.UTF_8.displayName());
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            if (!token.startsWith(SecurityConstant.HEAD)) {
                throw CodeException.builder().code(40003).msg(request.getPath().value() + " miss bearer").build();
            }
            token = token.substring(SecurityConstant.HEAD.length());
        } else if (StringUtils.hasLength(paramToken)) {
            token = paramToken;
        } else {
            throw CodeException.builder().code(40003).msg(request.getPath().value() + " miss token").build();
        }

        Claims claims = JWTUtils.verifyJavaWebToken(token, jwtProperties.getSecretKey());
        if (Objects.isNull(claims)) {
            throw CodeException.builder().code(40003).msg(request.getPath().value() + " token error").build();
        }

        String userIdStr = claims.getSubject();
        if (!StringUtils.hasLength(userIdStr)) {
            throw CodeException.builder().code(40004).msg(request.getPath().value() + " invalid token").build();
        }

        Date expiredTime = claims.getExpiration();
        if (expiredTime == null || (System.currentTimeMillis() > expiredTime.getTime())) {
            throw CodeException.builder().code(40004).msg(request.getPath().value() + " jwt token expire").build();
        }

        String tokenCacheKey = JWTUtils.getTokenCacheKey(userIdStr, token);
        securityCacheUtil.del(tokenCacheKey);

        return Mono.empty();
    }
}
